VGA-Kurs - Part #3 "T.C.P.'s Beginner's Guide To VGA Coding"(TM) ist wieder da mit Teil III! Zu Beginn als Appetith„ppchen eine erneut schnellere PutPixel-Routine. Die ist jetzt meiner Meinung nach nicht schneller zu machen (h”chstens in 386er Code). Wer anderer Meinung ist, soll seine L”sung hier pr„sentieren. Wichtig bei dieser Prozedur ist, das stets ein GLOBALE Konstante 'VGA : word = $A000;' deklariert wird. const VGA : word = $A000; procedure PutPixel(x,y:word;c:byte);assembler; asm mov es,VGA mov di,x mov dx,y mov bx,dx shl dx,8 shl bx,6 add dx,bx add di,dx mov al,c stosb end; Auáerdem noch unentbehrlich: Eine GetPixel-Funktion. Diese ermittelt den Farbwert des Pixels, der an den angegebenen Koordinaten steht. function GetPixel(x,y:word) : byte;assembler; asm mov es,vga mov di,x mov dx,y mov bx,dx shl dx,8 shl bx,6 add dx,bx add di,dx mov al,es:[di] mov [bp-1],al end; Diese Funktion verf„hrt bei der Berechnung des Offsets des Pixels haargenau so wie die Schwester-Prozedur. Nur am Schluá gibt es eine kleine Žnderung. Statt den Pixel zu schreiben wird der Farbwert, der bekanntlich an der Adresse A000h:Y*320+X steht, in AL ausgelesen und dann als Funktionsergebnis zurckgeliefert. Nun w„re es vielleicht an der Zeit, daá sich der geneigte Leser eine Unit mit den besprochenen Routinen zusammenstellt, um die Beispiele einfacher kompilieren zu k”nnen und eigene Programme schneller zu erstellen. Aber nun zur Sache: In dieser Ausgabe werden wir uns einem h„ufig besprochenen und geheimnisumwitterten Thema widmen: Dem Scrolly. Was ein Scrolly ist, weiá wahrscheinlich jeder. Eine Zeichenkette wird zum angenehmeren Lesen von rechts nach links ge(sc)rollt. Dabei wird zuerst ein Teil der Laufschrift am rechten Rand des Screens gezeigt, dann ein Stck nach links bewegt und schlieálich der n„chste Teil angefgt. Einfaches Prinzip, groáe Wirkung. Um wirklich zu beeindrucken, sollte der Scrolly aber nicht nur einfach scrollen sondern z.B. noch ein Plasma im Hintergrund bewegt werden oder Žhnliches. Beginnen wir im Praxisteil mit einem einfachen Textmode-Scrolly. Der Text wird in der Konstanten Text abgelegt. Nun wird innerhalb der Schleife zuerst der Buchstabe des Textes auf den Screen geschrieben (Zeile 13), dessen Nummer in der Variablen Charno steht. Nun wird die komplette Zeile ab dem 2. Zeichen um 1 Zeichen nach links verschoben. Dazu folgendes: Wir haben in der ersten Ausgabe gelernt wie der Bildschirmspeicher ab Adresse A000h organisiert ist. Zur Erinnerung: Die Pixelinformationen sind hintereinander in einem 64000 Byte groáen Bereich abgelegt. Die Offset-Adresse l„át sich nach der Formel Offset = Y-Koord * 320 + X-Koord berechnen. Nun, im Textmodus ist dies so „hnlich. Der Bildschirmspeicher liegt an der Adresse B800h (B000h bei Hercules), und ist 4000 Byte groá. Bei Segment B800, Offset 0 liegt also der ASCII-Code des ersten Zeichens auf dem Bildschirm. Steht nun auf dem Screen bei den Koords (0,0) ein "A", so findet man bei Adresse $B800:0 den Wert 65, also den ASCII-Code von "A". An Adresse $B800:1 liegt nun aber nicht, wie zu vermuten w„re, der ASCII-Code des zweiten Zeichens, Koords (1,0), sondern das sog. Attributbyte des ersten Zeichens. Dieses beeinhaltet die Farbe und die Art des Zeichens, z.B. rot und blinkend. Um nun dem "A" die Farbe Rot und das Attribut "blinkend" zuzuweisen, rechnet man den Wert fr die Farbe Rot (4) und fr "blinkend" (128) zusammen, erh„lt 132, und schreibt diesen Wert an die Adresse $B800:1. Unter Pascal kann man zum Žndern von Textfarbe und Attribut auch die Prozedur "Textcolor" der Unit Crt benutzen. Rot und blinkend stellt man z.B. ein mit "Textcolor(red+blink);". Die Formel zum Berechnen der Adresse eines Zeichens ist also: Adresse = (Y-Koord * 80 + X-Koord) * 2. Zurck zu unserem Scrolly. Mit der Standard-Prozedur "Move" werden nun ab der Adresse $B800:1922 158 Bytes (79 Zeichen) nach $B800:1920 (also um ein Zeichen nach links) kopiert. Nun wird nur noch die Nummer des aktuellen Buchstabens erh”ht und berprft, ob die Zeichenkette bereits am Ende ist, und wenn ja wird Charno wieder auf 1 gesetzt. Dies geht solange weiter, bis eine Taste gedrckt wird. Achso: Generell ist es bei Scrollies natrlich wichtig, auf ein entsprechend flssiges Scrolling zu achten. Dies besorgt die Prozedur "WaitRetrace", die wir schon in der letzten Ausgabe kennengelernt haben. Ihr k”nnt das "Waitretrace" ja mal aus der Schleife entfernen und durch ein "Delay(10)" ersetzen, und zusehen, was fr ein Geruckel dabei herauskommt. Augenbeleidigend! program Scrolly1; uses crt; const Text : string = 'Hallo, dies ist ein Test Sc'+ 'rolly, der sich solange wie'+ 'derholt, bis ein Taste gedr'+ 'ckt wird..................'; var Charno : byte; procedure WaitRetrace;assembler; asm mov dx,3DAh @l1: in al,dx and al,08h jz @l1 @l2: in al,dx and al,08h jz @l2 end; begin Charno := 1; { Erstes Zeichen des Scrolltextes } clrscr; { Bildschirm l”schen } gotoxy(80,13); { Zum letzten Zeichen in Zeile 13 } repeat WaitRetrace; write(Text[Charno]); { Zeichen schreiben } move(mem[$B800:1922],mem[$B800:1920],158); { 79 Zeichen um 1 Zeichen nach links schieben } inc(Charno); { N„chstes Zeichen } if Charno > length(Text) then Charno := 1; { Wenn Zeichenkette am Ende, von vorne beginnen } gotoxy(80,13); { Wieder zum Ausgangspunkt } until keypressed; readkey; end. Mal wieder eine Anmerkung: Der GotoXY-Befehl benutzt, wahrscheinlich um den Umgang mit der Prozedur fr Anf„nger zu erleichtern, ein Koordinatensystem, das bei (1,1) beginnt. Wenn ihr also zu (20,20) wollt, mát ihr GotoXY(21,21) eingeben. Dies ist oft sehr verwirrend, und man kann leider nur Abhilfe schaffen, wenn man sich eine eigene GotoXY-Variante schreibt. So, das waren erstmal die Grundlagen. Aber was wir wollten, war ja ein Scrolly im VGA-Modus. Doch hier ist das Ganze nicht mehr so einfach. Als erstes ben”tigen wir einen Font, denn sonst haben wir ja nichts zum Scrollen. Ich gehe mal davon aus, daá keiner von euch gewillt ist, sich jetzt einen eigenen Font zu zeichnen, also mssen wir uns einen aus dem BIOS-Rom holen und so einrichten, daá wir frei auf ihn zugreifen k”nnen. var FontSeg,FontOfs : word; procedure GetFont;assembler; asm mov ax,1130h mov bh,3 { Font-Nummer fr 8x8-Font } int 10h mov FontSeg,es { Segment, in dem der Font abgelegt ist } mov FontOfs,bp { Offset des Fonts } end; Rufen wir diese Prozedur auf, haben wir an Adresse FontSeg:FontOfs den 8 mal 8 Pixel-BIOS-Font. Ein Buchstabe ist also 8 Byte groá. Im ersten Byte stehen in den einzelnen Bits die Informationen der ersten Zeile des Buchstabens, im zweiten Byte die Bits der zweiten Reihe usw. Um nun festzustellen, ob im Font ein Pixel gesetzt (Bit=1) ist oder nicht (Bit=0), muá man die Bits mit den entsprechenden Potenzen von 2 und-verknpfen. Im folgenden VGA-Modus-Scrolly wird also zuerst die erste Spalte des ersten Buchstabens angezeigt, auf den Retrace gewartet, dann die komplette Zeile um ein Pixel nach links verschoben und die n„chste Spalte auf den Screen geschrieben. Die Nummer des Buchstabens steht wieder in Charno, die Spalte in Charpos. In Character wird der ASCII-Code des aktuellen Zeichens abgelegt. program Scrolly2; uses crt; const VGA = $A000; Bits : array[0..7] of byte = (128,64,32,16,8,4,2,1); Text : string = 'Ein Scrolly im VGA-Modus 13'+ 'h, er scrollt und scrollt u'+ 'nd scrollt.................'; var FontSeg,FontOfs : word; { Hier die Prozeduren GetFont und WaitRetrace einsetzen } procedure Scroll; var I,J : word; CharPos,CharNo,Color,Character : byte; begin CharNo := 1; { Anfangsposition } repeat Character := ord(Text[CharNo]); { ASCII-Code holen } for CharPos := 0 to 7 do begin { 8x8 Pixel je } for I := 0 to 7 do begin { Zeichen } if mem[FontSeg:FontOfs+(Character*8)+I] and Bits[CharPos] <> 0 then Color := 31 else Color := black; { Wenn daá entsprechende Bit gesetzt ist, } { dann Farbe weiá (31) setzen, andernfalls } { schwarz. } mem[$A000:((100+I)*320)+319] := Color; { Pixel setzen } end; WaitRetrace; for J := 0 to 7 do for I := 0 to 318 do mem[$A000:((100+J)*320)+I] := mem[$A000:((100+J)*320)+1+I]; { Alles um einen Pixel nach links bewegen } end; inc(CharNo); if CharNo > length(Text) then CharNo := 1; until keypressed; readkey; end; begin GetFont; asm mov ax,13h; int 10h end; { VGA-Modus 13h } Scroll; asm mov ax,03h; int 10h end; { Textmodus } end. Dieser Scrolly wrde wahrscheinlich in einem Demo sehr wenig Anklang finden, da der Font nicht gerade zu den allerhbschesten z„hlt. Auáerdem ist er bloá einfarbig. Letzteres Problem l„át sich allerdings sehr leicht aus der Welt schaffen. Deklariert einfach ein Array of byte mit den gewnschten Farben der Zeilen. Zum Beispiel: const Colors : array[0..7] of byte = (25,27,29,31,31,29,27,25); Nun ersetzt ihr im Listing die Zeile "Bits[CharPos] <> 0 then Color := 31" durch "Bits[CharPos] <> 0 then Color := Colors[I]". Dadurch wird jeder Zeile des Scrollies ein der Reihe nach der entsprechende Farbwert aus dem Colors-Array zugewiesen. In diesem Fall beeinhaltet das Array einen Grau-Verlauf. šbrigens stellen in der Standard-Palette (s. Teil II) die Farben Nummer 16 bis 31 einen Grau-Verlauf bereit (16=Schwarz, 31=Weiá), den man bei Bedarf nutzen sollte. Wer das Beispiel aufmerksam studiert hat, dem wird es auch nicht schwerfallen, einen eigenen Font einzubauen. So viel zu horizontalen Scrollies. Natrlich gibt es auch vertikale Scrollies (eines der besten Beispiele ist am Ende von 2nd Reality zu bestaunen). Diese sind im Modus 13h zwar ruckelfrei realisierbar, doch durch das Fehlen weiterer Bildschirmseiten (wie im Mode-X, s.u.) wird dies ein sinnloses Unterfangen, denn es kann maximal eine Seite gescrollt werden, dann ist das Ende der Fahnenstange erreicht. Hier jedoch trotzdem ein solcher Scrolly im Mode 13h, nur um das Prinzip zu verdeutlichen: program VertScrolly; uses crt; const VGA : word = $A000; var i,j : word; c : char; { Hier Prozedur WaitRetrace einfgen } procedure SetStart(Adresse:word);assembler; asm mov dx,3D4h { CRTC-Indexregister } mov al,0Ch mov ah,byte ptr Adresse + 1 out dx,ax mov al,0Dh mov ah,byte ptr Adresse out dx,ax end; begin randomize; { "Zufalls"zahlen generieren } asm mov ax,13h; int 10h end; { VGA-Modus 13h } for i := 0 to 65535 do mem[vga:i] := random(256); { Bildschirm fllen } i := 0; j := 80; repeat if keypressed then begin c := readkey; if c = ' ' then j := -j { Richtung umkehren } else exit; end; delay(10); { Ohne Delay zu schnell } waitretrace; SetStart(i); { Neue Startadresse setzen } inc(i,j); { Z„hler erh”hen/vermindern } until keypressed; readkey; asm mov ax,03h; int 10h end; { Textmodus } end. Die Prozedur SetStart bildet den Kern dieses Programms. Sie stellt mittels des CRTC ein, an welcher Adresse der Bilschirm 'beginnt'. Dabei wird kein einziges Byte kopiert oder bewegt, sondern bloá die Adresse, ab der die VGA die im Bildschirmspeicher abgelegten Informationen auf den Monitor bringt, ver„ndert. Ruft man sie also mit SetStart(32000); auf, so wird der Bildschirminhalt erst ab dem Offset 32000 dargestellt. In diesem Modus gibt es aber wie gesagt den Nebeneffekt, daá hier in der zweiten H„lfte des Bildschirms der bersprungene Teil wieder angefgt wird. Einen anderen Bereich der Scrollies, das Full-Screen-Scrolling, werden wir sp„ter behandeln, wenn wir zum Thema "Mode-X" kommen. Der Modus 13h hat n„mlich das Manko, daá er nur eine Bildschirmseite untersttzt und so den Speicher der VGA-Karten (heutzutage mind. 512KB) kaum ausnutzt. Deshalb ntzt das Scrolling des gesamten Bildschirminhalt mittels des CRTC (Cathode Ray Tube Controller) wenig, da immer nur derselbe Bildschirminhalt gescrollt werden kann. Im Mode-X (oder Chain-4) dagegen hat man bis zu 4 Virtuelle Bildschirme zur Verfgung. Aber das besprechen wir noch ausfhrlich in einem der n„chsten Teile. Also, das war's zum Thema Scrollies, Thema von Part IV wahrscheinlich: Sprites. [ This text copyright (c) 1995-96 Johannes Spohr. All rights reserved. ] [ Distributed exclusively through PC-Heimwerker, Verlag Thomas Eberle. ] [ ] [ No part of this document may be reproduced, transmitted, ] [ transcribed, stored in a retrieval system, or translated into any ] [ human or computer language, in any form or by any means; electronic, ] [ mechanical, magnetic, optical, chemical, manual or otherwise, ] [ without the expressed written permission of the author. ] [ ] [ The information contained in this text is believed to be correct. ] [ The text is subject to change without notice and does not represent ] [ a commitment on the part of the author. ] [ The author does not make a warranty of any kind with regard to this ] [ material, including, but not limited to, the implied warranties of ] [ merchantability and fitness for a particular purpose. The author ] [ shall not be liable for errors contained herein or for incidental or ] [ consequential damages in connection with the furnishing, performance ] [ or use of this material. ]